use crate::key_manager::algorithm::factory::algorithm_factory::KeyAlgorithm;
use crate::key_manager::error::KeyManagerError;
use crate::register_algorithm;
use anyhow::{Context, Result};
use libsm::sm2::signature::{SigCtx, Signature};
use openssl::pkey::{PKey, Private, Public};

#[derive(Debug)]
pub struct Sm2Algorithm;

impl KeyAlgorithm for Sm2Algorithm {
    fn derive_public(&self, private: &Vec<u8>) -> Result<Vec<u8>, KeyManagerError> {
        let ctx = SigCtx::new();
        let sk = ctx
            .load_seckey(private)
            .map_err(|e| KeyManagerError::new(format!("Invalid SM2 private key: {}", e)))?;
        let pk = ctx
            .pk_from_sk(&sk)
            .map_err(|e| KeyManagerError::new(format!("Failed to derive public key: {}", e)))?;
        ctx.serialize_pubkey(&pk, true)
            .map_err(|e| KeyManagerError::new(format!("Serialization failed: {}", e)))
    }

    fn sign(&self, private: &PKey<Private>, data: Vec<u8>) -> Result<Vec<u8>, KeyManagerError> {
        let der = private
            .private_key_to_der()
            .context("Failed to export private key to DER")?;

        let ctx = SigCtx::new();
        let sk = ctx
            .load_seckey(&der)
            .map_err(|e| KeyManagerError::new(format!("Invalid SM2 private key: {}", e)))?;
        let pk = ctx
            .pk_from_sk(&sk)
            .map_err(|e| KeyManagerError::new(format!("Failed to derive public key: {}", e)))?;

        let signature = ctx
            .sign(&data, &sk, &pk)
            .map_err(|e| KeyManagerError::new(format!("SM2 signing failed: {}", e)))?;

        Ok(signature.der_encode())
    }

    fn verify(
        &self,
        public: &PKey<Public>,
        data: Vec<u8>,
        sig: Vec<u8>,
    ) -> Result<bool, KeyManagerError> {
        let der = public
            .public_key_to_der()
            .context("Failed to export public key to DER")?;

        let ctx = SigCtx::new();
        let pk = ctx
            .load_pubkey(&der)
            .map_err(|e| KeyManagerError::new(format!("Invalid SM2 public key: {}", e)))?;
        let signature = Signature::der_decode(&sig)
            .map_err(|e| KeyManagerError::new(format!("Invalid signature format: {}", e)))?;

        ctx.verify_raw(&data, &pk, &signature)
            .map_err(|e| KeyManagerError::new(format!("Verification failed: {}", e)))
    }
}

register_algorithm!(
    "sm2",
    (|args: &[&str]| -> Result<Box<dyn KeyAlgorithm>> {
        if !args.is_empty() {
            return Err(anyhow::anyhow!("SM2 algorithm does not accept parameters"));
        }
        Ok(Box::new(Sm2Algorithm))
    })
);
